package dao

import (
	"context"
	"encoding/json"
	"fmt"
	"gorm.io/gorm/clause"
	"inventory/api/inventory"
	"inventory/pkg/ecode"
	"time"

	"inventory/internal/model"
)

// FindProcessOrders 获取仓库下履约单列表
func (d *Dao) FindProcessOrders(ctx context.Context, warehouseID int, status []int,
	source inventory.SourceEnum, memberIDs []string, extractKey string,
	startDate, endDate, expectStartDate, exceptEndDate, deliverStartDate, deliverEndDate time.Time,
	pageNo, pageSize int, orderNumber, extOrderNumber string, direction int) (
	total int64, orders []*model.ProcessOrder, err error) {
	db := d.TransContext(ctx).Model(&model.ProcessOrder{}).Where(
		"warehouse_id = ? and submit_time between ? and ?  and status in ? and deliver_type != ? ",
		warehouseID, startDate, endDate.Add(time.Hour*24-time.Second), status, int(inventory.DeliverType_SKIP))
	if extOrderNumber != "" {
		if len(orderNumber) < 4 {
			err = ecode.BadRequest("单号模糊搜索至少需要4位数字")
		}
		db = db.Where("ext_order_number like ?", "%"+extOrderNumber+"%")
	}
	if orderNumber != "" {
		if len(orderNumber) < 4 {
			err = ecode.BadRequest("单号模糊搜索至少需要4位数字")
		}
		db = db.Where("order_number like ?", "%"+orderNumber+"%")
	}
	if direction != 0 {
		db = db.Where("direction = ?", direction)
	}
	if source != inventory.SourceEnum__SOURCE_ALL {
		db = db.Where("source = ?", int(source))
	}
	if len(memberIDs) != 0 {
		db = db.Where("member_id in ?", memberIDs)
	}
	if extractKey != "" {
		db = db.Where("remark->>'extract_key' = ?", extractKey)
	}
	if !expectStartDate.IsZero() && !exceptEndDate.IsZero() {
		db = db.Where("expect_deliver_date between ? and ?",
			expectStartDate, exceptEndDate.Add(time.Hour*24-time.Second))
	}
	if !deliverStartDate.IsZero() && !deliverEndDate.IsZero() {
		db = db.Where("deliver_time between ? and ?",
			deliverStartDate, deliverEndDate.Add(time.Hour*24-time.Second))
	}
	if err = db.Count(&total).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	err = db.Order("id desc").Offset((pageNo - 1) * pageSize).Limit(pageSize).Find(&orders).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// FindProcessOrderDetailsByOrderIDs 获取采购详情
func (d *Dao) FindProcessOrderDetailsByOrderIDs(ctx context.Context, orderIDs []uint64) (details []*model.ProcessDetail, err error) {
	err = d.TransContext(ctx).Model(&model.ProcessDetail{}).Where("order_id in ?", orderIDs).Find(&details).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// FindProcessOrdersByOrderNumbers 基于单号查找履约单
func (d *Dao) FindProcessOrdersByOrderNumbers(ctx context.Context, orderNumbers []string) (orders []*model.ProcessOrder, err error) {
	err = d.TransContext(ctx).Model(&model.ProcessOrder{}).Where("order_number in ?",
		orderNumbers).Find(&orders).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// FindProcessOrdersByFreezeOrderID 基于冻结单查找履约单
func (d *Dao) FindProcessOrdersByFreezeOrderID(ctx context.Context, freezeOrderID uint64) (orders []*model.ProcessOrder, err error) {
	err = d.TransContext(ctx).Model(&model.ProcessOrder{}).Where("freeze_order_id = ?",
		freezeOrderID).Find(&orders).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// FindUnfinishedPickupProcessOrders 获取未完结履约自提单
func (d *Dao) FindUnfinishedPickupProcessOrders(ctx context.Context, warehouseID uint32) (orders []*model.ProcessOrder, err error) {
	err = d.TransContext(ctx).Model(&model.ProcessOrder{}).Where(
		"warehouse_id = ? and status in ?", warehouseID,
		[]int{
			int(inventory.ProcessOrderStatus_ProcessOrderWaitStock),
			int(inventory.ProcessOrderStatus_ProcessOrderWaitMentioned)}).Offset(
		0).Limit(1).Find(&orders).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// GetProcessOrder 获取履约单
func (d *Dao) GetProcessOrder(ctx context.Context, orderNumber string) (process *model.ProcessOrder, err error) {
	process = &model.ProcessOrder{}
	err = d.TransContext(ctx).Model(&model.ProcessOrder{}).Where("order_number = ?", orderNumber).Take(&process).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// GetProcessOrderCountByExtractKey 获取履约单
func (d *Dao) GetProcessOrderCountByExtractKey(ctx context.Context, warehouseID int, extractKey string) (count int64, err error) {
	err = d.TransContext(ctx).Model(&model.ProcessOrder{}).Where("warehouse_id = ? and extract_key = ?",
		warehouseID, extractKey).Count(&count).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// GetProcessOrderWithoutSgID 无sgId获取履约单
func (d *Dao) GetProcessOrderWithoutSgID(ctx context.Context, orderNumber string) (process *model.ProcessOrder, err error) {
	process = &model.ProcessOrder{}
	err = d.TransContext(ctx).Model(&model.ProcessOrder{}).Where("order_number = ?", orderNumber).Take(&process).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// GetProcessOrderByExtOrderNumberAndSource 外部单号获取履约单
func (d *Dao) GetProcessOrderByExtOrderNumberAndSource(ctx context.Context, extOrderNumber string, source int) (order *model.ProcessOrder, err error) {
	order = &model.ProcessOrder{}
	err = d.TransContext(ctx).Model(&model.ProcessOrder{}).Clauses(clause.Locking{Strength: "UPDATE"}).Where(
		"ext_order_number = ? and source = ?", extOrderNumber, source).Take(&order).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// LockProcessOrderByExtOrderNumberAndSource 外部单号获取履约单
func (d *Dao) LockProcessOrderByExtOrderNumberAndSource(ctx context.Context, extOrderNumber string, source int) (order *model.ProcessOrder, err error) {
	order = &model.ProcessOrder{}
	err = d.TransContext(ctx).Model(&model.ProcessOrder{}).Where("ext_order_number = ? and source = ?", extOrderNumber, source).Take(&order).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// FindProcessOrderDetails 获取履约单详情
func (d *Dao) FindProcessOrderDetails(ctx context.Context, orderID uint64) (details []*model.ProcessDetail, err error) {
	err = d.TransContext(ctx).Model(&model.ProcessDetail{}).Where("order_id = ?", orderID).Find(&details).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// GetExpressOrderByOutTradeNo 获取配送单通过out_trade_no
func (d *Dao) GetExpressOrderByOutTradeNo(ctx context.Context, outTradeNo string) (expressOrder *model.ExpressOrder, err error) {
	expressOrder = &model.ExpressOrder{}
	err = d.TransContext(ctx).Model(&model.ExpressOrder{}).Where("out_trade_no = ?", outTradeNo).Take(&expressOrder).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// GetExpressOrderByID 获取配送单
func (d *Dao) GetExpressOrderByID(ctx context.Context, orderID uint64) (expressOrder *model.ExpressOrder, err error) {
	expressOrder = &model.ExpressOrder{}
	err = d.TransContext(ctx).Model(model.ExpressOrder{ID: orderID}).Where("id = ?", orderID).Take(expressOrder).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// UpdateProcessDetailStatus 更新履约单详情状态
func (d *Dao) UpdateProcessDetailStatus(ctx context.Context, orderID uint64, status int, now time.Time) (err error) {
	err = d.TransContext(ctx).Model(model.ProcessDetail{OrderID: orderID}).Where("order_id = ?", orderID).Updates(
		map[string]interface{}{
			"status":      status,
			"update_time": now,
		}).Error
	err = d.processSqlError(err)
	return
}

// GetPlatform 获取平台信息
func (d *Dao) GetPlatform(ctx context.Context, platformID uint32) (platform *model.ExpressPlatform, err error) {
	platform = &model.ExpressPlatform{}
	err = d.TransContext(ctx).Model(&model.ExpressPlatform{}).Where("id = ?", platformID).Take(&platform).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// FindExpressOrdersByOrderNumbers 获取配送单
func (d *Dao) FindExpressOrdersByOrderNumbers(ctx context.Context, orderNumbers []string) (orders []*model.ExpressOrder, err error) {
	err = d.TransContext(ctx).Model(&model.ExpressOrder{}).Where("order_number in ?", orderNumbers).Find(&orders).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

// FindPlatformByIDs 获取平台
func (d *Dao) FindPlatformByIDs(ctx context.Context, platformIDs []int) (platforms []*model.ExpressPlatform, err error) {
	err = d.TransContext(ctx).Model(&model.ExpressPlatform{}).Where("id in ?", platformIDs).Find(&platforms).Error
	if err != nil {
		err = d.processSqlError(err)
	}
	return
}

type materialRemark struct {
	Name string `json:"name"`
	Code string `json:"code"`
	Unit string `json:"unit"`
}

type materialView struct {
	ID int `json:"id"`
	materialRemark
}

// BuildProcessMaterialRemarks 构造物料remarks
func (d *Dao) BuildProcessMaterialRemarks(ctx context.Context, materialIDs []uint64) (remarks map[string]json.RawMessage, err error) {
	var views []materialView
	err = d.TransContext(ctx).Raw(
		`SELECT m.id, m.name, CONCAT(tt.code, '-', t.code, '-', m.code) as code, u.name as unit
		FROM inv_material as m JOIN inv_material_type as t ON m.mt_id = t.id 
        JOIN inv_material_type as tt ON t.parent_id = tt.id 
        JOIN inv_unit as u ON m.unit = u.id
        WHERE m.id in ?`,
		materialIDs).Scan(&views).Error
	if err != nil {
		err = d.processSqlError(err)
		return
	}
	remarks = make(map[string]json.RawMessage)
	for _, v := range views {
		remark, _ := json.Marshal(v.materialRemark)
		remarks[fmt.Sprintf("m-%d", v.ID)] = remark
	}
	return
}
